use crate::IResult;
use std::fmt::{Debug, Display};

use super::u2_int;





pub const MICROS_MIN: i32 = 0;
pub const MICROS_MAX: i32 = 999_999;


pub const MICROS_PER_MILLIS: i32 = 1000;
pub const MILLIS_PER_SEC: i32 = 1000;
/// 1000_000
pub const MICROS_PER_SEC: i32 = 1000_000;

pub const MICROS_PER_HOUR: i64 = 3600_000_000_i64;

pub const MICROS_PER_DAY: i64 = 86400_000_000_i64;

/// Micros in one sec, 
/// range [0, 999_999],
/// when micros = ABC_DEF_i32, it's milli(range [0, 999]) is ABC, and its micro(range [0, 999]) is DEF
/// for short: i=milli, o=micro
#[derive(serde::Deserialize, serde::Serialize, Clone, Copy, Default, PartialEq, Eq)]
pub struct MicrosOneSec {
    /// 不足1秒部分的总微秒数
    /// when micros = ABC_DEF_i32, it's milli(range [0, 999]) is ABC, and its micro(range [0, 999]) is DEF
    micros: i32,
}
impl MicrosOneSec {
    pub fn from_micros_unsafe(micros_sec: i32) -> Self {
        if 0<= micros_sec && micros_sec <= MICROS_MAX {
            Self { micros: micros_sec }
        } else {
            let (_secs, micros) = u2_int::divide_remain_i32(micros_sec, MICROS_PER_SEC);
            Self { micros }
        }
        
    }
    pub fn from_micros_i64_unsafe(micros_sec: i64) -> Self {
        if 0<= micros_sec && micros_sec <= MICROS_MAX as i64 {
            Self { micros: micros_sec as i32 }
        } else {
            let (_secs, micros) = u2_int::divide_remain_i64(micros_sec, MICROS_PER_SEC as i64);
            Self { micros: micros as i32 }
        }
        
    }
    pub fn min() -> Self {
        Self { micros: MICROS_MIN }
    }
    pub fn max() -> Self {
        Self { micros: MICROS_MAX }
    }

    pub fn set_min(&mut self) {
        self.micros = 0;
    }
}
impl MicrosOneSec {
    /// i=milli
    /// o=micro
    pub fn from_io(milli: u16, micro: u16) -> IResult<Self> {
        if milli > 999 {
            return Err("MicrosOneSec::from_io: milli must be in [0, 999]")?;
        }
        if micro > 999 {
            return Err("MicrosOneSec::from_io: micro must be in [0, 999]")?;
        }
        let micros = milli as i32 * MICROS_PER_MILLIS + micro as i32;

        Ok(Self { micros })
    }
    /// unsafe function, we don't judge the value range both of milli and micro
    /// i=milli
    /// o=micro
    pub fn from_io_unsafe(milli: u16, micro: u16) -> Self {
        let milli = if milli > 999 {
            (milli % 1000) as i32
        } else {
            milli as i32
        };
        let micro = if micro > 999 {
            (micro % 1000) as i32
        } else {
            micro as i32
        };
        let micros = milli * MICROS_PER_MILLIS + micro;
        Self { micros }
    }
    /// we set micro=0 defaultly, i=milli
    pub fn from_i(milli: u16) -> IResult<Self> {
        if milli > 999 {
            return Err("MicrosOneSec::from_i: milli must be in [0, 999]")?;
        }
        let micros = milli as i32 * MICROS_PER_MILLIS;

        Ok(Self { micros })
    }
    /// unsafe function, we don't judge the value range of milli, we set micro=0 defaultly
    /// i=milli
    /// o=micro
    pub fn from_i_unsafe(milli: u16) -> Self {
        let milli = if milli > 999 {
            (milli % 1000) as i32
        } else {
            milli as i32
        };
        let micros = milli * MICROS_PER_MILLIS;
        Self { micros }
    }



}
impl MicrosOneSec {
    /// 不足1秒部分的总微秒数
    pub fn micros_inner(&self) -> i32 {
        self.micros
    }
    pub fn milli(&self) -> u16 {
        (self.micros / MICROS_PER_MILLIS) as u16
    }
    pub fn micro(&self) -> u16 {
        (self.micros % MICROS_PER_MILLIS) as u16
    }
    /// for short: i=milli, o=micro
    pub fn io_i32(&self) -> (i32, i32) {
        let milli = self.micros / MICROS_PER_MILLIS;
        let micro = self.micros % MICROS_PER_MILLIS;
        (milli, micro)
    }


    /// for short: i=milli, o=micro
    pub fn io(&self) -> (u16, u16) {
        let milli = self.micros / MICROS_PER_MILLIS;
        let micro = self.micros % MICROS_PER_MILLIS;
        (milli as u16, micro as u16)
    }


    pub fn to_print(&self) -> String {
        let (milli, micro) = self.io();
        format!("({milli:0>3}){micro:0>3}")
    }

}
impl MicrosOneSec {
    pub fn micros_from(&self, tm_before: &Self) -> i32 {
        self.micros - tm_before.micros
    }
    /// micro part is omitted by dividing 1000
    pub fn millis_from_i32(&self, tm_before: &Self) -> i32 {
        (self.micros - tm_before.micros) / MICROS_PER_MILLIS
    }
    pub fn millis_from_f64(&self, tm_before: &Self) -> f64 {
        (self.micros - tm_before.micros) as f64 / MICROS_PER_MILLIS as f64
    }
    pub fn seconds_from(&self, tm_before: &Self) -> f64 {
        (self.micros - tm_before.micros) as f64 / MICROS_PER_SEC as f64
    }
}

impl Debug for MicrosOneSec {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("MicrosOneSec").field("print", &self.to_print()).field("micros", &self.micros).finish()
    }
}
impl Display for MicrosOneSec {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.to_print())
    }
}

pub fn format_micros(micros: i64) -> String{
    let (minus, micros) = if micros < 0 {
        ("-", -micros)
    } else {
        ("", micros)
    };
    if micros < 1000 {
        format!("{minus:0>3}{micros:0>3}")
    } else {
        let (secs_millis, micros) = u2_int::divide_remain_i64(micros, 1000);
        if secs_millis < 1000 {
            format!("{minus}({secs_millis:0>3}){micros:0>3}")
        } else {
            let (secs, millis) = u2_int::divide_remain_i64(secs_millis, 1000);
            format!("{minus}{secs: >3}({millis:0>3}){micros:0>3}")
        }
    }
}

impl MicrosOneSec {
    #[cfg(test)]
    pub(crate) fn test_generation_print() -> String {
        println!("MicrosOneSec::test_generation_print: you should make sure that all assert! codes is passed");
        let mut buf = format!("MicrosOneSec::test_generation_print: inner micros range from {MICROS_MIN} to {MICROS_MAX}:\n", );
        let mut milli_current = 0;
        let mut micro_current = 0;
        for micros in MICROS_MIN..(MICROS_MAX + 1) {
            let micros_onesec = Self { micros };
            let (milli, micro) = micros_onesec.io();
            assert!(milli == milli_current && micro == micro_current);
            micro_current += 1;
            if micro_current == 1000 {
                micro_current = 0;
                milli_current += 1;
            }
            buf.push_str(&format!("\t{micros:0>6}\t{}\n", micros_onesec.to_print()));
        }
        assert!(1000 == milli_current && 0 == micro_current);

        buf.push_str("MicrosOneSec::test_generation_print: milli range from 0 to 999, micro range from 0 to 999:\n");
        let mut micros_current = 0;
        for milli in 0..(999 + 1) {
            for micro in 0..(999 + 1) {
                let micros_onesec = MicrosOneSec::from_io_unsafe(milli, micro);
                assert!(micros_onesec.micros_inner() == micros_current);
                micros_current += 1;
                buf.push_str(&format!("\t{milli:0>3} & {micro:0>3} ==> {:0>6}\t{}\n", micros_onesec.micros_inner(), micros_onesec.to_print()));
            }
        }
        assert!(1000_000 == micros_current);




        buf
    }
}



